/*!	 action.h
**	 Template File
**
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**	Copyright (c) 2008 Chris Moore
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifndef __SYNFIG_APP_ACTION_H
#define __SYNFIG_APP_ACTION_H

#include "action_param.h"

#define ACTION_MODULE_EXT public: \
	static const char name__[], local_name__[], version__[], cvs_id__[], task__[]; \
	static const Category category__; \
	static const int priority__; \
	static Action::Base *create(); \
	virtual synfig::String get_name()const;	\
	virtual synfig::String get_local_name()const;

#define ACTION_SET_NAME(class,x) const char class::name__[]=x

#define ACTION_SET_CATEGORY(class,x) const Category class::category__(x)

#define ACTION_SET_TASK(class,x) const char class::task__[]=x

#define ACTION_SET_PRIORITY(class,x) const int class::priority__=x

#define ACTION_SET_LOCAL_NAME(class,x) const char class::local_name__[]=x

#define ACTION_SET_VERSION(class,x) const char class::version__[]=x

#define ACTION_SET_CVS_ID(class,x) const char class::cvs_id__[]=x

// don't define get_local_name() - allow the action code to define its own
#define ACTION_INIT_NO_GET_LOCAL_NAME(class)			  \
	Action::Base* class::create() { return new class(); } \
	synfig::String class::get_name()const { return name__; }

#define ACTION_INIT(class)				 \
	ACTION_INIT_NO_GET_LOCAL_NAME(class) \
	synfig::String class::get_local_name()const { return dgettext("synfigstudio",local_name__); }

namespace synfig
{
class ProgressCallback;
class Canvas;
}; // END of namespace synfig

namespace synfigapp
{

class Instance;
class Main;

namespace Action
{

class System;

// Exception class, thrown when redoing or undoing an action
class Error
{
public:
    enum Type {
        TYPE_UNKNOWN,
        TYPE_UNABLE,
        TYPE_BADPARAM,
        TYPE_CRITICAL,
        TYPE_NOTREADY,
        TYPE_BUG,

        TYPE_END
    };
private:

    Type type_;
    synfig::String desc_;

public:

    Error(Type type, const char *format, ...):
        type_(type)
    {
        va_list args;
        va_start(args, format);
        desc_ = etl::vstrprintf(format, args);
    }

    Error(const char *format, ...):
        type_(TYPE_UNKNOWN)
    {
        va_list args;
        va_start(args, format);
        desc_ = etl::vstrprintf(format, args);
    }

    Error(Type type = TYPE_UNABLE):
        type_(type)
    {
    }

    Type get_type()const
    {
        return type_;
    }
    synfig::String get_desc()const
    {
        return desc_;
    }

}; // END of class Action::Error

class Param;
class ParamList;
class ParamDesc;
class ParamVocab;

// Action Category
enum Category {
    CATEGORY_NONE			= 0,
    CATEGORY_LAYER			= (1 << 0),
    CATEGORY_CANVAS			= (1 << 1),
    CATEGORY_WAYPOINT		= (1 << 2),
    CATEGORY_ACTIVEPOINT	= (1 << 3),
    CATEGORY_VALUEDESC		= (1 << 4),
    CATEGORY_VALUENODE		= (1 << 5),
    CATEGORY_KEYFRAME		= (1 << 6),
    CATEGORY_GROUP			= (1 << 7),
    CATEGORY_BEZIER			= (1 << 8),

    CATEGORY_OTHER			= (1 << 12),

    CATEGORY_DRAG			= (1 << 24),

    CATEGORY_HIDDEN			= (1 << 31),
    CATEGORY_ALL			= (~0) - (1 << 31)		// < All categories (EXCEPT HIDDEN)
}; // END of enum Category

inline Category operator|(Category lhs, Category rhs)
{
    return static_cast<Category>(int(lhs) | int(rhs));
}

// Top-level base class for all actions
/*!	An action should implement the following functions:
**	- static bool is_candidate(const ParamList &x);
**		- 	Checks the ParamList to see if this action could be performed.
**	- static ParamVocab get_param_vocab();
**		-	Yields the ParamVocab object which describes what
**			this action needs before it can perform the act.
**	- static Action::Base* create();
**		-	Factory for creating this action from a ParamList
**
*/
class Base : public etl::shared_object
{
protected:
    Base() { }

public:
    virtual ~Base() { };

    // This function will throw an Action::Error() on failure
    virtual void perform() = 0;

    virtual bool set_param(const synfig::String& /*name*/, const Param &)
    {
        return false;
    }
    virtual bool get_param(const synfig::String& /*name*/, Param &)
    {
        return false;
    }
    virtual bool is_ready()const = 0;

    virtual synfig::String get_name()const = 0;
    virtual synfig::String get_local_name()const
    {
        return get_name();
    }

    void set_param_list(const ParamList &);

    static synfig::String get_layer_descriptions(const std::list<synfig::Layer::Handle> layers, synfig::String singular_prefix = "", synfig::String plural_prefix = "");
    static synfig::String get_layer_descriptions(const std::list<std::pair<synfig::Layer::Handle, int> > layers, synfig::String singular_prefix = "", synfig::String plural_prefix = "");
}; // END of class Action::Base

typedef Action::Base* (*Factory)();
typedef bool (*CandidateChecker)(const ParamList &x);
typedef ParamVocab(*GetParamVocab)();

typedef etl::handle<Base> Handle;

// Undoable Action Base Class
class Undoable : public Base
{
    friend class System;
    bool active_;

protected:
    Undoable();

#ifdef _DEBUG
    ~Undoable();
#endif

private:
    void set_active(bool x)
    {
        active_ = x;
    }

public:

    // This function will throw an Action::Error() on failure
    virtual void undo() = 0;

    bool is_active()const
    {
        return active_;
    }

#ifdef _DEBUG
    virtual void ref()const;
    virtual bool unref()const;
#endif
}; // END of class Action::Undoable

// Action base class for canvas-specific actions
class CanvasSpecific
{
private:
    bool is_dirty_;
    EditMode	mode_;

    etl::loose_handle<synfigapp::CanvasInterface> canvas_interface_;
    synfig::Canvas::Handle canvas_;

protected:
    CanvasSpecific(const synfig::Canvas::Handle &canvas): is_dirty_(true), mode_(MODE_UNDEFINED), canvas_(canvas) { }
    CanvasSpecific(): is_dirty_(true), mode_(MODE_UNDEFINED) { }

    virtual ~CanvasSpecific() { };

public:

    void set_canvas(synfig::Canvas::Handle x)
    {
        canvas_ = x;
    }
    void set_canvas_interface(etl::loose_handle<synfigapp::CanvasInterface> x)
    {
        canvas_interface_ = x;
    }

    synfig::Canvas::Handle get_canvas()const
    {
        return canvas_;
    }
    etl::loose_handle<synfigapp::CanvasInterface> get_canvas_interface()const
    {
        return canvas_interface_;
    }

    static ParamVocab get_param_vocab();
    virtual bool set_param(const synfig::String& name, const Param &);
    virtual bool get_param(const synfig::String& /*name*/, Param &)
    {
        return false;
    }
    virtual bool is_ready()const;

    EditMode get_edit_mode()const;

    void set_edit_mode(EditMode x)
    {
        mode_ = x;
    }

    bool is_dirty()const
    {
        return is_dirty_;
    }
    void set_dirty(bool x = true)
    {
        is_dirty_ = x;
    }

}; // END of class Action::CanvasSpecific

typedef std::list< etl::handle<Action::Undoable> > ActionList;

/*!	\class synfigapp::Action::Super
**	 Super-Action base class for actions composed of several other actions.
**
**	Actions deriving from this class should only implement prepare(), and
**	NOT implement perform() or undo().
*/
class Super : public Undoable, public CanvasSpecific
{
    ActionList action_list_;

public:

    ActionList &action_list()
    {
        return action_list_;
    }
    const ActionList &action_list()const
    {
        return action_list_;
    }

    virtual void prepare() = 0;

    void clear()
    {
        action_list().clear();
    }

    bool first_time()const
    {
        return action_list_.empty();
    }

    void add_action(etl::handle<Undoable> action);
    void add_action_front(etl::handle<Undoable> action);

    void add_action(etl::handle<Base> action)
    {
        etl::handle<Undoable> undoable = etl::handle<Undoable>::cast_dynamic(action);
        assert(undoable);
        add_action(undoable);
    }

    void add_action_front(etl::handle<Base> action)
    {
        etl::handle<Undoable> undoable = etl::handle<Undoable>::cast_dynamic(action);
        assert(undoable);
        add_action_front(undoable);
    }

    virtual void perform();
    virtual void undo();

}; // END of class Action::Super

class Group : public Super
{
    synfig::String name_;

    ActionList action_list_;
protected:
    bool ready_;
public:
    Group(const synfig::String &str = "Group");
    virtual ~Group();

    virtual synfig::String get_name()const
    {
        return name_;
    }

    virtual void prepare() { };

    virtual bool set_param(const synfig::String& /*name*/, const Param &)const
    {
        return false;
    }
    virtual bool is_ready()const
    {
        return ready_;
    }

    void set_name(std::string&x)
    {
        name_ = x;
    }
}; // END of class Action::Group

struct BookEntry {
    synfig::String 	name;
    synfig::String 	local_name;
    synfig::String 	version;
    synfig::String 	task;
    int 			priority;
    Category		category;
    Factory 		factory;
    CandidateChecker	is_candidate;
    GetParamVocab	get_param_vocab;

    bool operator<(const BookEntry &rhs)const
    {
        return priority < rhs.priority;
    }
}; // END of struct BookEntry

typedef std::map<synfig::String, BookEntry> Book;

class CandidateList : public std::list<BookEntry>
{
public:
    iterator find(const synfig::String& x);
    const_iterator find(const synfig::String& x)const
    {
        return const_cast<CandidateList*>(this)->find(x);
    }
};

Book& book();

Handle create(const synfig::String &name);

// Compiles a list of potential candidate actions with the given \a param_list and \a category
CandidateList compile_candidate_list(const ParamList& param_list, Category category = CATEGORY_ALL);

/*!	\class synfigapp::Action::Main
**	 \writeme
**
**	\writeme
*/
class Main
{
    friend class synfigapp::Main;

    Main();

public:
    ~Main();

}; // END of class Action::Main

}; // END of namespace Action

}; // END of namespace synfigapp

#endif